import { all, call, put } from "redux-saga/effects";
import { createAction, callNativeApi } from "@common/utils";
import router from "../router";
import { SET_ERR_MSG } from "../store/global-state/action-types";

function* processErr(error) {
  if (error.response?.data?.responseHeader) {
    let { errorMessage, errorCode } = error.response.data.responseHeader;
    if ("ERR_EXPIRED_TOKEN" === errorCode) {
      router.navigate("/");
    }
    yield put(createAction(SET_ERR_MSG, errorMessage));
  } else if (error.code === 'ECONNABORTED') {
    yield put(createAction(SET_ERR_MSG, "请求超时, 请稍候重试!"));
  } else {
    yield put(createAction(SET_ERR_MSG, "发生未知错误, 请联系管理员!"));
  }
  console.log(error);
}

function* processNativeApiErr(resp) {
  yield put(createAction(SET_ERR_MSG, resp.errorMessage));
}

export function* asyncRequest({
  effects,
  isConcurrency,
  concurrencyRespActions,
  concurrencyCallback,
}) {
  try {
    document.getElementById("load-box").style.visibility = "visible";

    if (isConcurrency) {
      const tasks = effects.map((effect) => call(effect.api, effect.payload));
      const results = yield all(tasks);
      if (concurrencyRespActions) {
        if (concurrencyRespActions.length === tasks.length) {
          // 当请求与响应处理函数数量一致时，分别传入对应的响应数据并执行响应处理函数
          for (let i = 0; i < concurrencyRespActions.length; i++) {
            let type = concurrencyRespActions[i];
            yield put(createAction(type, results[i].data.payload));
          }
        } else {
          // 当请求与响应处理函数数量不一致时，仅使用第1个响应处理函数处理全部响应数据
          let allRespData = results.map((r) => r.data.payload);
          yield put(createAction(concurrencyRespActions[0], allRespData));
        }
      }
      if (concurrencyCallback) {
        concurrencyCallback();
      }
    } else {
      for (let i = 0; i < effects.length; i++) {
        let { api, payload, respActions, callback } = effects[i];
        let resp = yield call(api, payload);

        if (respActions) {
          for (let i = 0; i < respActions.length; i++) {
            let type = respActions[i];
            yield put(createAction(type, resp.data.payload));
          }
        }

        if (callback) {
          callback();
        }
      }
    }
  } catch (error) {
    yield* processErr(error);
  } finally {
    document.getElementById("load-box").style.visibility = "hidden";
  }
}

export function* callNativeFunc({ effects }) {
  try {
    document.getElementById("load-box").style.visibility = "visible";
    const previousResp = [];
    
    for (let i = 0; i < effects.length; i++) {
      let { buildPayload, respActions, callback } = effects[i];
      let resp = null;
      let respData = null;
      if (buildPayload) {
        let payload = buildPayload(previousResp);
        resp = yield call(callNativeApi, payload);
      } else {
        resp = yield call(callNativeApi, effects[i].payload);
      }
      
      // mock response is http response, real response just from native api
      respData = resp.data ? resp.data : resp;

      if (respData.result.status === "FAILURE") {
        yield processNativeApiErr(respData.result);
        break;
      }

      if (respActions) {
        for (let i = 0; i < respActions.length; i++) {
          let type = respActions[i];
          yield put(createAction(type, respData.payload));
        }
      }

      if (callback) {
        callback(respData);
      }

      previousResp.push(respData);
    }
  } catch (error) {
    console.log(error);
  } finally {
    document.getElementById("load-box").style.visibility = "hidden";
  }
}

export const asyncCallNativeFunc = async ({ effects }) => {
  try {
    document.getElementById("load-box").style.visibility = "visible";
    const previousResp = [];
    
    for (let i = 0; i < effects.length; i++) {
      let { buildPayload, respActions, callback } = effects[i];
      let resp = null;
      let respData = null;
      if (buildPayload) {
        let payload = buildPayload(previousResp);
        resp =  await callNativeApi(payload);
      } else {
        resp = await callNativeApi(effects[i].payload);
      }
      
      // mock response is http response, real response just from native api
      respData = resp.data ? resp.data : resp;

      if (respData.result.status === "FAILURE") {
        window.app.store.dispatch(createAction(SET_ERR_MSG, respData.result.errorMessage));
        break;
      }

      if (respActions) {
        for (let i = 0; i < respActions.length; i++) {
          let type = respActions[i];
          window.app.store.dispatch(createAction(type, respData.payload));
        }
      }

      if (callback) {
        callback(respData);
      }

      previousResp.push(respData);
    }
  } catch (error) {
    console.log(error);
  } finally {
    document.getElementById("load-box").style.visibility = "hidden";
  }
};